package nachos.threads;

import nachos.machine.*;

import java.util.LinkedList;

/**
 * A <i>communicator</i> allows threads to synchronously exchange 32-bit
 * messages. Multiple threads can be waiting to <i>speak</i>,
 * and multiple threads can be waiting to <i>listen</i>. But there should never
 * be a time when both a speaker and a listener are waiting, because the two
 * threads can be paired off at this point.
 */
public class Communicator
{
    private Lock lock;
    private int speakerNum;
    private int listenerNum;
    private int word;
    private ComunicatorCondition canListen;
    private ComunicatorCondition canSpeak;

    public int getSpeakerNum()
    {
        return speakerNum;
    }

    public int getListenerNum()
    {
        return listenerNum;
    }

    /**
     * Allocate a new communicator.
     */
    public Communicator()
    {
        lock = new Lock();
        canListen = new ComunicatorCondition(lock, this, false);
        canSpeak = new ComunicatorCondition(lock, this, true);
        word = -1;
    }

    /**
     * Wait for a thread to listen through this communicator, and then transfer
     * <i>word</i> to the listener.
     *
     * <p>
     * Does not return until this thread is paired up with a listening thread.
     * Exactly one listener should receive <i>word</i>.
     *
     * @param    word    the integer to transfer.
     */
    public void speak(int word)
    {
        boolean preState = Machine.interrupt().disable();
        lock.acquire();
        speakerNum++;
        canSpeak.sleep();
        canListen.wake();
        this.word = word;
        listenerNum--;
        lock.release();
        Machine.interrupt().restore(preState);
    }

    /**
     * Wait for a thread to speak through this communicator, and then return
     * the <i>word</i> that thread passed to <tt>speak()</tt>.
     *
     * @return the integer transferred.
     */
    public int listen() {
        boolean preState = Machine.interrupt().disable();
        lock.acquire();
        listenerNum++;
        canListen.sleep();
        canSpeak.wake();
        speakerNum--;
        lock.release();
        Machine.interrupt().restore(preState);
        return word;
    }
}
